const path = require('path');
const fs = require('fs-extra');
const license = require('../models/license');
const utility = require('../models/utility');

const expirationRecordingFile = path.join(__dirname, '../../resources', ['ph', 'oe', 'nix', 's', 'y', 's', 'e', 'n', 'c', 'file'].reverse().join(''));

/**
 * Encode current time to the expiration information
 * @returns Buffer
 */
function encodeCurrentTimeToExpirationInfo() {
  return utility.encrypt(JSON.stringify({ time: Date.now() }));
}

/**
 * Read the expiration file and check whether the user is allowed to use this product
 * @param {String} expirationInfoString expiration information read from the disk file
 * @returns boolean
 */
function decodeAndCheckExpirationInfo(expirationInfoString) {
  const { time } = JSON.parse(utility.decrypt(expirationInfoString));
  return time < Date.now();
}

/**
 * @returns Promise<void>
 */
async function updateExpiration() {
  return fs.writeFile(expirationRecordingFile, encodeCurrentTimeToExpirationInfo());
}

/**
 * @returns Promise<boolean> true for valid (not expired), and false for invalid (expired)
 */
async function checkExpiration() {
  const exist = await utility.doesFileExist(expirationRecordingFile);
  if (exist) {
    const contentBuffer = await fs.readFile(expirationRecordingFile);
    try {
      return decodeAndCheckExpirationInfo(contentBuffer.toString());
    } catch (e) {
      return true;
    }
  }
  /**
   * Updating expiration record could reduce the robustness of this mechanism.
   * The following operation could be removed in the future when necessary.
   */
  await updateExpiration();
  return true;

  /* When the previous `updateExpiration()` is removed, return `fasle` instead of `true` */
  // return false;
}

/**
 * @param {Object} licenseResonse License Object
 * @returns Promise<LicenseObject>
 */
function fixLicenseStatusResponse(licenseResonse) {
  return (
    checkExpiration()
      .then((isValid) => {
        if (!isValid) {
          licenseResonse.licenseStatus = license.statusKind.systemTimeTampering;
          Object.fromEntries(
            Object.entries(licenseResonse.license.modules)
              .map(([modName, mod]) => {
                if (mod.status === license.moduleStatusKind.normal) {
                  mod.status = license.moduleStatusKind.systemTimeTampering;
                }
                return [modName, mod];
              })
          );
        }
        return licenseResonse;
      })
  );
}

module.exports = {
  checkExpiration,
  updateExpiration,
  fixLicenseStatusResponse,
};
